package cn.org.wifit.imserver.zookeeper.handle;

import java.io.ByteArrayInputStream;
import java.io.ObjectInputStream;
import java.util.HashMap;
import java.util.List;

import org.apache.zookeeper.ZooKeeper;
import org.apache.zookeeper.data.Stat;

import cn.org.wifit.core.entity.xml.IPX;
import cn.org.wifit.core.entity.xml.Server;
import cn.org.wifit.core.utils.JSONUtils;
import cn.org.wifit.core.zookeeper.NodeReadWatcherHandle;
import cn.org.wifit.core.zookeeper.ServerNodeTree;

/**
 * @author yinwenjie
 */
public class IMServerWatcherHandle implements NodeReadWatcherHandle {
	/**
	 * 
	 */
	private ZooKeeper zk;

	public ZooKeeper getZk() {
		return zk;
	}

	public void setZk(ZooKeeper zk) {
		this.zk = zk;
	}

	/* (non-Javadoc)
	 * @see cn.org.wifit.core.zookeeper.NodeReadWatcherHandle#doServerIpxChanged(java.lang.String)
	 */
	@SuppressWarnings("unchecked")
	@Override
	public void doServerIpxChanged(String parentPath) throws Exception {
		/*
		 * 由于监控特性，我们并不知道是那个ipx节点发生了变化，只知道上一级的节点信息（从parentPath得到）
		 * 我们也不是到是增加了一个ipx还是减少了一个ipx
		 * 所以，处理过程为：
		 * 1、从本地ServerNodeTree得到当前server下的所有ipx信息
		 * 2、从远程zookeeper上面得到所有的/IMServer/server下所有ipx的信息
		 * 3、以zookeeper上得到的ipx为基础，比较本地ipx。
		 * 	如果最后发现本地ipx多出，则表示是远程ipx减少了，删除本地多出来的即可
		 * 	如果最后发现远程ipx多出，则表示是远程ipx多出来了，向本地增加新的ipx即可。
		 * */
		ServerNodeTree serverNodeTree = ServerNodeTree.getNewInstance();
		String json = serverNodeTree.GetIMServers(parentPath.substring(parentPath.length() - 10, parentPath.length()));
		
		List<IPX> localIPXs = JSONUtils.toBean(json, IPX.class);
		List<String> zkIPXs = this.zk.getChildren(parentPath, false); 
		//如果条件成立，说明是远程zookeeper上面有节点增加
		if(localIPXs == null || localIPXs.size() == 0 || localIPXs.size() < zkIPXs.size()) {
			//一般zookeeper上面增加的节点，都在最后，所以从zkIPXs的最后来检查就行了
			for(int index = zkIPXs.size() - 1 ; index >= 0 ; index--) {
				String ipxpath = zkIPXs.get(0);
				String fullpath = parentPath + "/" + ipxpath;
				String ipxsequence = ipxpath.substring(ipxpath.length() - 10, ipxpath.length());
				String serversequence = parentPath.substring(parentPath.length() - 10, parentPath.length());
				
				//如果条件成立，说明新增的节点就是这个了，向serverNodeTree增加，并终止循环即可
				if(!serverNodeTree.isServeripxExist(serversequence, ipxsequence)) {
					//获取数据
					byte[] databytes = this.zk.getData(fullpath, false, null);
					ObjectInputStream in = new ObjectInputStream(new ByteArrayInputStream(databytes));
					HashMap<String, Object> ipxdata = (HashMap<String, Object>)in.readObject();
					in.close();
					
					String ip = ipxdata.get("ip").toString();
					Integer port = (Integer)ipxdata.get("port");
					Integer userport = (Integer)ipxdata.get("userport");
					
					//开始添加
					serverNodeTree.AddIMServerIPX(serversequence, ipxsequence, ip, port, userport);
					break;
				}
			}
		}
		
		//如果条件成立，说明是远程zookeeper上面有节点减少
		else if(zkIPXs == null || zkIPXs.size() == 0 || zkIPXs.size() < localIPXs.size()) {
			//由于不知道server下的哪个ipx减少了，所以基于localIPXs从第一个开始检查
			for(int index = 0 ; index < localIPXs.size() ; index++) {
				IPX localIpx = localIPXs.get(index);
				String ipxsequence = localIpx.getSequence();
				String serversequence = parentPath.substring(parentPath.length() - 10, parentPath.length());
				String fullpath = parentPath + "/ipx" + ipxsequence;
				
				//判断远程有没有
				Stat stat = this.zk.exists(fullpath, false);
				//如果条件成立，说明远程zk没有
				if(stat == null) {
					serverNodeTree.removeIMServerIPX(serversequence, ipxsequence);
					break;
				}
			}
		}
	}

	/* (non-Javadoc)
	 * @see cn.org.wifit.core.zookeeper.NodeReadWatcherHandle#doServerChanged()
	 */
	@SuppressWarnings("unchecked")
	@Override
	public void doServerChanged() throws Exception {
		/*
		 * 由于监控特性，我们并不知道是IMServer下的哪个server节点发生了变化，只知道上一级的节点信息（从parentPath得到）
		 * 我们也不是到是增加了一个server还是减少了一个server
		 * 所以，处理过程为：
		 * 1、从本地ServerNodeTree得到当前所有的server信息
		 * 2、从远程zookeeper上面得到所有的/IMServer/server信息
		 * 3、以zookeeper上得到的server为基础，比较本地server。
		 * 	如果本地server数量多于 zk上取得的server数量，说明zk上面的server节点减少了（这种情况在正常的使用过程中不会出现，除非是在zk服务器上强制删除）。
		 * 	如果本地server数量少于 zk上取得的server数量，说明zk上面的server节点增加了
		 * */
		ServerNodeTree serverNodeTree = ServerNodeTree.getNewInstance();
		String json = serverNodeTree.GetIMServers();
		List<Server> localServers = JSONUtils.toBean(json, Server.class);
		List<String> zkServers = this.zk.getChildren("/IMServer", false);
		
		//如果条件成立，说明远程有server节点增加。
		if(localServers == null || localServers.size() == 0 || zkServers.size() > localServers.size()) {
			//一般远程增加的节点都是zkServers的最后一个原色，所以从最后一个开始检查就行了
			for(int index = zkServers.size() - 1 ; index >= 0 ; index--) {
				String serverpath = zkServers.get(0);
				String fullpath = "/IMServer/" + serverpath;
				String serversequence = serverpath.substring(serverpath.length() - 10, serverpath.length());
				
				//如果条件成立，说明新增的节点就是这个了，向serverNodeTree增加，并终止循环即可
				if(!serverNodeTree.isServerExist(serversequence)) {
					//获取数据
					byte[] databytes = this.zk.getData(fullpath, false, null);
					ObjectInputStream in = new ObjectInputStream(new ByteArrayInputStream(databytes));
					HashMap<String, Integer> serverdata = (HashMap<String, Integer>)in.readObject();
					in.close();
					
					Integer fromid = serverdata.get("fromid");
					Integer toid = serverdata.get("toid");
					
					//开始添加
					serverNodeTree.AddIMServer(serversequence, fromid, toid);
					break;
				}
			}
		}
		//如果条件成立，说明远程有server节点减少
		else if(zkServers == null || zkServers.size() == 0 || zkServers.size() < localServers.size()) {
			//由于不知道IMServer下的哪个server减少了，所以基于localServer从第一个开始检查
			for(int index = 0 ; index < localServers.size() ; index++) {
				Server localServer = localServers.get(index);
				String serversequence = localServer.getSequence();
				String fullpath = "/IMServer/server" + serversequence;
				
				//判断远程有没有
				Stat stat = this.zk.exists(fullpath, false);
				//如果条件成立，说明远程zk没有
				if(stat == null) {
					serverNodeTree.removeServer(serversequence);
					break;
				}
			}
		}
	}

	@Override
	public void doUserIpxChanged(String parentPath) throws Exception {
		/*
		 * 按照设计IMServerWatcherHandle不可能监听到这样的情况，这个方法体里面直接空着就行
		 * */
	}
}